#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <jni.h>
#include <sys/system_properties.h>
#include <android/log.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include "xhook/src/xhook.h"

int (*oldaccess)(const char *pathname, int mode);
int (*oldopen)(const char *pathname, int flags);
FILE* (*oldfopen)(const char *filename, const char *mode);
int (*oldfaccessat)(int fd, const char *pathname, int name, int flag);
int (*oldopenat)(int dirfd, const char *pathname, int flags, mode_t mode);
prop_info* (*old_system_property_find)(const char* name);
int (*old_system_property_get)(const char *name, char *value);
int (*old_system_property_read)(const prop_info *pi, char *name, char *value);
FILE* (*oldpopen)( const char *command , const char *type );

static char* get_my_value(const char* name);
static char *check_property(const char *name);
static FILE *get_popen_file(const char *command);
static char *get_prop_file(const char *command);

static int new_access(const char * pathname, int mode){
    __android_log_print(3, "Nativehook", "Nativehook_access unhook %s %d", pathname, mode);
    if (strcmp(pathname, "/system/bin/su") == 0
        || strcmp(pathname, "/system/xbin/su") == 0
        || strcmp(pathname, "/system/sbin/su") == 0
        || strcmp(pathname, "/sbin/su") == 0
        || strcmp(pathname, "/vendor/bin/") == 0
        || strcmp(pathname,"/sys/qemu_trace") == 0
        || strcmp(pathname,"/system/bin/qemu-props") == 0
        || strcmp(pathname, "/system/lib/libc_malloc_debug_qemu.so") == 0
        || strcmp(pathname, "/dev/socket/baseband_genyd") == 0
        || strcmp(pathname, "/dev/socket/genyd") == 0
        || strcmp(pathname, "/dev/qemu_pipe") == 0
        || strcmp(pathname, "/dev/socket/qemud") == 0){
        return -1;
    }
    if(strstr(pathname, "Superuser.apk") != NULL
        || strstr(pathname, "XposedBridge.jar") != NULL
        || strstr(pathname, "edxp.so") != NULL
        || strstr(pathname, "edxp.jar") != NULL){
        return -1;
    }
    return access(pathname,mode);
}

static int new_open(const char * pathname, int flags){
    __android_log_print(3, "Nativehook", "Nativehook_open unhook %s %d", pathname, flags);
    if ( strcmp(pathname, "/sys/class/net/wlan0/address") == 0
         || strcmp(pathname, "/sys/class/net/eth0/address") == 0
         || strcmp(pathname, "/sys/devices/virtual/net/wlan0/address") == 0
         || strcmp(pathname, "/system/bin/su") == 0
         || strcmp(pathname, "/system/xbin/su") == 0 )
    {
        __android_log_print(3, "Nativehook", "Nativehook_open Fail %s %d", pathname, flags);
        return -1;
    }
    return oldopen(pathname,flags);
}

static FILE* new_fopen(const char *filename, const char *mode){
    __android_log_print(3, "Nativehook", "Nativehook_fopen unhook %s %s", filename, mode);
    return oldfopen(filename,mode);
}

static int new_faccessat(int fd, const char *pathname, int name, int flag){
    __android_log_print(3, "Nativehook", "Nativehook_faccessat unhook %d %s %d %d",fd, pathname,name,flag);
    return oldfaccessat(fd,pathname,name,flag);
}

static int new_openat(int dirfd, const char *pathname, int flags, mode_t mode){
    __android_log_print(3, "Nativehook", "Nativehook_openat unhook");
    return oldopenat(dirfd,pathname,flags,mode);
}

static int new_system_property_get(const char *name, char *value){
    __android_log_print(3, "Nativehook", "_system_property_get unhook %s %s",name,value);
    const char *value_;
    char *value__tmp;
    value_ = (const char*)check_property(name);
    __android_log_print(3, "Nativehook", "_system_property_get finded %s %s",name,value_);
    if(strcmp(value_,"-1")==0){
        return old_system_property_get(name,value);
    }else if(strcmp(value_,"-2")==0){
        return old_system_property_get("xxx.xxx.xxx.xxx",value);
    }
    __android_log_print(3, "Nativehook","Nativehook_system_property_get hooked1 %s %s\n",value_,value);
    value__tmp = (char*)value_;
    __android_log_print(3, "Nativehook","Nativehook_system_property_get hooked2 %s %s\n",value_,value);
    unsigned int len_ = sizeof(value__tmp);
    unsigned int len = strlen(value__tmp);
    memcpy(value, value__tmp,len+1);
    free(value__tmp);
    __android_log_print(3, "Nativehook","_system_property_get hooked3 %s %s\n",value_,value);
    return len_;
}

static int new_system_property_read(const prop_info *pi, char *name, char *value) {
    __android_log_print(3, "Nativehook", "_system_property_get unhook %s %s",name,value);
    const char *value_;
    char *value__tmp;
    value_ = (const char*)check_property(name);
    __android_log_print(3, "Nativehook", "_system_property_get finded %s %s",name,value_);
    if(strcmp(value_,"-1")==0){
        return old_system_property_read(pi,name,value);
    }else if(strcmp(value_,"-2")==0){
        return old_system_property_read(pi,"xxx.xxx.xxx.xxx",value);
    }
    __android_log_print(3, "Nativehook","Nativehook_system_property_get hooked1 %s %s\n",value_,value);
    value__tmp = (char*)value_;
    __android_log_print(3, "Nativehook","Nativehook_system_property_get hooked2 %s %s\n",value_,value);
    unsigned int len_ = sizeof(value__tmp);
    unsigned int len = strlen(value__tmp);
    memcpy(value, value__tmp,len+1);
    free(value__tmp);
    __android_log_print(3, "Nativehook","_system_property_get hooked3 %s %s\n",value_,value);
    return len_;
}

const prop_info* new_system_property_find(const char* name){
    __android_log_print(3, "Nativehook", "Nativehook_system_property_find unhook %s",name);
    const char *value_;
    value_ = (const char*)check_property(name);
    __android_log_print(3, "Nativehook", "Nativehook_system_property_find find %s %s",name,value_);
    if(strcmp(value_,"-2")==0){
        return old_system_property_find("xxx.xxx.xxx.xxx");
    }
    return old_system_property_find(name);
}

static FILE* newpopen(const char *command ,const char *type ){
    __android_log_print(3, "Nativehook", "Nativehook_popen unhook %s %s",command,type);
    if(strstr(command,"cat")!=NULL){
        if(strstr(command,"/proc/cpuinfo")!=NULL){
            return get_popen_file("proc_cpuinfo");
        }
        if(strstr(command,"/proc/version")!=NULL){
            return get_popen_file("proc_version");
        }
        if(strstr(command,"cpuinfo_max_freq")!=NULL){
            return get_popen_file("cpuinfo_max_freq");
        }
        if(strstr(command,"cpuinfo_min_freq")!=NULL){
            return get_popen_file("cpuinfo_min_freq");
        }
        if(strstr(command,"/proc/sys/kernel/random/boot_id")){
            return get_popen_file("random_boot_id");
        }
        if(strstr(command,"/proc/tty/drivers")){
            return get_popen_file("tty_drivers");
        }
    }
    if(strstr(command,"getprop")!=NULL){
        char *value = get_prop_file(command);
        __android_log_print(3, "Nativehook", "Nativehook_popen unhook %s %s",command,value);
        return get_popen_file(value);
    }
    return oldpopen(command,type);
}

static char* get_my_value(const char* name)
{
    FILE *fp;
    long file_len;
    char* sd = "/mnt/sdcard/fixclub/";
    char path[strlen(sd)];
    strcpy(path,sd);
    strcat(path,name);
    __android_log_print(3, "Nativehook", "Nativehook_get_my_value open %s",path);
    if((fp = fopen(path, "r")) == NULL)
    {
        __android_log_print(3, "Nativehook", "Nativehook_get_my_value can not open %s",path);
        return NULL;
    }
    fseek(fp, 0, SEEK_END);
    file_len = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    char *str_buf = (char *)malloc(file_len + 1);
    fread(str_buf, file_len+1, 1, fp);
    fclose(fp);
    return str_buf;
}

static FILE* get_popen_file(const char *command){
    FILE *fp;
    char* sd = "/mnt/sdcard/fixclub/";
    char path[strlen(sd)];
    strcpy(path,sd);
    strcat(path,command);
    __android_log_print(3, "Nativehook", "Nativehook_get_popen open %s",path);
    if((fp = fopen(path, "r")) == NULL)
    {
        __android_log_print(3, "Nativehook", "Nativehook_ can not open %s",path);
        return NULL;
    }
    return fp;
}

static char* check_property(const char *name){
    char *value = "-1";
    char *value_= "00";
    //设备信息
    if(strcmp(name, "ro.product.brand") == 0){
        value_ = get_my_value("brand");
    }else if(strcmp(name, "ro.product.manufacturer") == 0){
        value_ = get_my_value("manufacturer");
    }else if(strcmp(name, "ro.product.model") == 0){
        value_ = get_my_value("model");
    }else if(strcmp(name, "ro.build.product") == 0){
        value_ = get_my_value("product");
    }else if(strcmp(name, "ro.product.name") == 0){
        value_ = get_my_value("name");
    }else if(strcmp(name, "ro.product.device") == 0){
        value_ = get_my_value("device");
    }else if(strcmp(name, "ro.build.version.sdk") == 0){
        value_ = get_my_value("sdk");
    }else if(strcmp(name, "ro.build.version.release") == 0){
        value_ = get_my_value("release");
    }else if(strcmp(name, "ro.build.display.id") == 0){
        value_ = get_my_value("id");
    }else if(strcmp(name, "ro.product.locale.language") == 0){
        value_ = get_my_value("language");
    }else if(strcmp(name, "ro.product.locale.region") == 0){
        value_ = get_my_value("region");
    }else if(strcmp(name, "ro.build.tags") == 0){
        value_ = get_my_value("tags");
    }else if(strcmp(name, "ro.build.fingerprint") == 0){
        value_ = get_my_value("fingerprint");
    }else if(strcmp(name, "ro.build.description") == 0){
        value_ = get_my_value("description");
    }else if(strcmp(name, "ro.build.host") == 0){
        value_ = get_my_value("host");
    }else if(strcmp(name, "ro.build.version.incremental") == 0){
        value_ = get_my_value("incremental");
    }else if(strcmp(name, "ro.hardware") == 0){
        value_ = get_my_value("hardware");
    }else if(strcmp(name, "ro.product.cpu.abi") == 0){
        value_ = get_my_value("abi");
    }else if(strcmp(name, "ro.product.cpu.abi2") == 0){
        value_ = get_my_value("abi2");
    }else if(strcmp(name,"ro.product.cpu.abilist") == 0){
        value_ = get_my_value("abilist");
    }else if(strcmp(name,"ro.board.platform") == 0){
        value_ = get_my_value("platform");
    }
    //serialno
    if(strcmp(name,"ro.serialno") == 0||strcmp(name,"ro.boot.serialno") == 0){
        value_ = get_my_value("serialno");
    }
    //deviceid
    if(strcmp(name,"ro.boot.deviceid") == 0||strcmp(name,"ro.deviceid") == 0){
        value_ = get_my_value("deviceid");
    }
    //meid
    if(strcmp(name,"gsm.meid") == 0
       ||strcmp(name,"gsm.device.meid") == 0
       ||strcmp(name,"persist.radio.meid") == 0
       ||strcmp(name,"ro.meizu.hardware.meid") == 0
       ||strcmp(name,"ril.cdma.meid") == 0
       ||strcmp(name,"persist.sys.meid") == 0
       ||strcmp(name,"ro.ril.oem.meid") == 0){
        value_ = get_my_value("meid");
    }
    //imei
    if(strcmp(name,"persist.radio.imei") == 0
       ||strcmp(name,"persist.sys.updater.imei") == 0
       ||strcmp(name,"persist.sys.vtouch.imei") == 0
       ||strcmp(name,"persist.sys.show.device.imei") == 0
       ||strcmp(name,"ril.gsm.imei") == 0
       ||strcmp(name,"ro.meizu.hardware.imei1") == 0
       ||strcmp(name,"ro.ril.miui.imei0") == 0
       ||strcmp(name,"persist.radio.imei1") == 0
       ||strcmp(name,"gsm.device.imei1") == 0
       ||strcmp(name,"gsm.imei1") == 0){
        value_ = get_my_value("imei1");
    }
    //imei2
    if(strcmp(name,"persist.radio.imei2") == 0
       ||strcmp(name,"ril.gsm.imei2") == 0
       ||strcmp(name,"ro.meizu.hardware.imei2") == 0
       ||strcmp(name,"ro.ril.miui.imei1") == 0
       ||strcmp(name,"persist.radio.imei2") == 0
       ||strcmp(name,"gsm.device.imei2") == 0
       ||strcmp(name,"gsm.imei2") == 0){
        value_ = get_my_value("imei2");
    }
    if(strcmp(value_,"00") != 0){
        size_t len = strlen(value_)+1;
        value = malloc(sizeof(char*));
        memcpy(value,value_,len);
        free(value_);
    }
    //虚拟机
    if(strcmp(name,"init.svc.vbox86-setup") == 0
       ||strcmp(name,"init.svc.droid4x") == 0
       ||strcmp(name,"init.svc.qemud") == 0
       ||strcmp(name,"init.svc.su_kpbs_daemon") == 0
       ||strcmp(name,"init.svc.noxd") == 0
       ||strcmp(name,"init.svc.ttVM_x86-setup") == 0
       ||strcmp(name,"init.svc.xxkmsg") == 0
       ||strcmp(name,"init.svc.microvirtd") == 0
       ||strcmp(name,"ro.kernel.android.qemud") == 0
       ||strcmp(name,"androVM.vbox_dpi") == 0
       ||strcmp(name,"androVM.vbox_graph_mode") == 0){
        value = "-2";
    }
    //虚拟机
    if(strcmp(name,"init.svc.qemud") == 0
       ||strcmp(name,"init.svc.qemu-props") == 0
       ||strcmp(name,"qemu.sf.fake_camera") == 0
       ||strcmp(name,"qemu.sf.lcd_density") == 0
       ||strcmp(name,"ro.kernel.qemu.gles") == 0
       ||strcmp(name,"ro.kernel.qemu") == 0
       ||strcmp(name,"ro.kernel.android.qemud") == 0
       ||strcmp(name,"qemu.hw.mainkeys") == 0){
        value = "-2";
    }
    return value;
}

static char* get_prop_file(const char *command){
    if(strstr(command, "ro.product.brand") != NULL){
        return"brand";
    }else if(strstr(command, "ro.product.manufacturer") != NULL){
        return "manufacturer";
    }else if(strstr(command, "ro.product.model") != NULL){
        return "model";
    }else if(strstr(command, "ro.build.product") != NULL){
        return "product";
    }else if(strstr(command, "ro.product.name") != NULL){
        return "name";
    }else if(strstr(command, "ro.product.device") != NULL){
        return "device";
    }else if(strstr(command, "ro.build.version.sdk") != NULL){
        return "sdk";
    }else if(strstr(command, "ro.build.version.release") != NULL){
        return "release";
    }else if(strstr(command, "ro.build.display.id") != NULL){
        return "id";
    }else if(strstr(command, "ro.product.locale.language") != NULL){
        return "language";
    }else if(strstr(command, "ro.product.locale.region") != NULL){
        return "region";
    }else if(strstr(command, "ro.build.tags") != NULL){
        return "tags";
    }else if(strstr(command, "ro.build.fingerprint") != NULL){
        return "fingerprint";
    }else if(strstr(command, "ro.build.description") != NULL){
        return "description";
    }else if(strstr(command, "ro.build.host") != NULL){
        return "host";
    }else if(strstr(command, "ro.build.version.incremental") != NULL){
        return "incremental";
    }else if(strstr(command, "ro.hardware") != NULL){
        return "hardware";
    }else if(strstr(command, "ro.product.cpu.abi") != NULL){
        return "abi";
    }else if(strstr(command, "ro.product.cpu.abi2") != NULL){
        return "abi2";
    }else if(strstr(command,"ro.board.platform") != NULL){
        return "platform";
    }
    //serialno
    if(strstr(command,"ro.serialno") != NULL||strstr(command,"ro.boot.serialno") != NULL){
        return "serialno";
    }

    if(strstr(command,"ro.build.version.emui")!= NULL){
        return "emui_version";
    }else if(strstr(command,"ro.vivo.os.build.display.id") != NULL){
        return "vivo_id";
    }else if(strstr(command,"ro.letv.release.version") != NULL){
        return "letv_version";
    }else if(strstr(command,"ro.miui.ui.version.name") != NULL){
        return "miui_version";
    }else if(strstr(command,"ro.vivo.product.version") != NULL){
        return "vivo_version";
    }else if(strstr(command,"ro.build.version.opporom") != NULL){
        return "oppo_version";
    }else if(strstr(command,"ro.build.uiversion") != NULL){
        return "360_version";
    }else if(strstr(command,"ro.gn.sv.version") != NULL){
        return "jinli_version";
    }

    if(strstr(command,"ro.boot.deviceid") != NULL){
        return "deviceid";
    }else if(strstr(command,"ro.deviceid") != NULL){
        return "deviceid";
    }

    //meid
    if(strstr(command,"gsm.meid") != NULL
       ||strstr(command,"gsm.device.meid") != NULL
       ||strstr(command,"persist.radio.meid") != NULL
       ||strstr(command,"ro.meizu.hardware.meid") != NULL
       ||strstr(command,"ril.cdma.meid") != NULL
       ||strstr(command,"persist.sys.meid") != NULL
       ||strstr(command,"ro.ril.oem.meid") != NULL){
        return "meid";
    }

    //imei
    if(strstr(command,"persist.radio.imei") != NULL
       ||strstr(command,"persist.sys.updater.imei") != NULL
       ||strstr(command,"persist.sys.vtouch.imei") != NULL
       ||strstr(command,"persist.sys.show.device.imei") != NULL
       ||strstr(command,"ril.gsm.imei") != NULL
       ||strstr(command,"ro.meizu.hardware.imei1") != NULL
       ||strstr(command,"ro.ril.miui.imei0") != NULL
       ||strstr(command,"persist.radio.imei1") != NULL
       ||strstr(command,"gsm.device.imei1") != NULL
       ||strstr(command,"gsm.imei1") != NULL){
        return "imei1";
    }

    if(strstr(command,"persist.radio.imei2") != NULL
       ||strstr(command,"ril.gsm.imei2") != NULL
       ||strstr(command,"ro.meizu.hardware.imei2") != NULL
       ||strstr(command,"ro.ril.miui.imei1") != NULL
       ||strstr(command,"persist.radio.imei2") != NULL
       ||strstr(command,"gsm.device.imei2") != NULL
       ||strstr(command,"gsm.imei2") != NULL){
        return "imei2";
    }

    if(strstr(command,"init.svc.vbox86-setup") != NULL//虚拟机
       ||strstr(command,"init.svc.droid4x") != NULL
       ||strstr(command,"init.svc.qemud") != NULL
       ||strstr(command,"init.svc.su_kpbs_daemon") != NULL
       ||strstr(command,"init.svc.noxd") != NULL
       ||strstr(command,"init.svc.ttVM_x86-setup") != NULL
       ||strstr(command,"init.svc.xxkmsg") != NULL
       ||strstr(command,"init.svc.microvirtd") != NULL
       ||strstr(command,"ro.kernel.android.qemud") != NULL
       ||strstr(command,"androVM.vbox_dpi") != NULL
       ||strstr(command,"androVM.vbox_graph_mode") != NULL){
        return "err_prop";
    }

    if(strstr(command,"init.svc.qemud") != NULL//虚拟机
       ||strstr(command,"init.svc.qemu-props") != NULL
       ||strstr(command,"qemu.sf.fake_camera") != NULL
       ||strstr(command,"qemu.sf.lcd_density") != NULL
       ||strstr(command,"ro.kernel.qemu.gles") != NULL
       ||strstr(command,"ro.kernel.qemu") != NULL
       ||strstr(command,"ro.kernel.android.qemud") != NULL
       ||strstr(command,"qemu.hw.mainkeys") != NULL){
        return "err_prop";
    }
    return "null";
}

void Java_com_junior_fixclub_party_NativeHandle_start(JNIEnv* env, jobject obj)
{
    (void)env;
    (void)obj;

    xhook_ignore(".*/libnativehook.so$", NULL);

    __android_log_print(3, "Nativehook", "startHook start hook");

    xhook_register("/data/.*\\.so$", "access",new_access ,(void**)(&oldaccess));

    xhook_register("/data/.*\\.so$", "open", new_open,(void**)(&oldopen));

    xhook_register("/data/data.*\\.so$", "fopen",new_fopen,(void**)(&oldfopen));

    xhook_register("/data/.*\\.so$", "faccessat",new_faccessat,(void**)(&oldfaccessat));

    xhook_register("/data/.*\\.so$", "openat",new_openat,(void**)(&oldopenat));

    xhook_register("/data/.*\\.so$", "__system_property_find",new_system_property_find,(void**)(&old_system_property_find));

    xhook_register("/data/.*\\.so$", "__system_property_get",new_system_property_get,(void**)(&old_system_property_get));

    xhook_register("/data/.*\\.so$", "__system_property_read",new_system_property_read,(void**)(&old_system_property_read));

    xhook_register("/data/.*\\.so", "popen",newpopen,(void**)(&oldpopen));

    xhook_refresh(1);
}

jint Java_com_junior_fixclub_party_NativeHandle_refresh(JNIEnv* env, jobject obj,jboolean flag)
{
    return xhook_refresh(flag);
}

void Java_com_junior_fixclub_party_NativeHandle_clear(JNIEnv* env, jobject obj)
{
    xhook_clear();
}

void Java_com_junior_fixclub_party_NativeHandle_enableDebug(JNIEnv* env, jobject obj,jboolean flag)
{
    xhook_enable_debug(flag);
}

void Java_com_junior_fixclub_party_NativeHandle_enableSigSegvProtection(JNIEnv* env, jobject obj,jboolean flag)
{
    xhook_enable_sigsegv_protection(flag);
}

jstring Java_com_junior_fixclub_party_NativeHandle_testValue(JNIEnv* env, jobject obj,jstring str)
{
    const char *name = (*env)->GetStringUTFChars(env, str, NULL);
    char value[100] = {'d','b','b','\0'};
    char *value_ = &value;
    new_system_property_get(name,value_);
    return (*env)->NewStringUTF(env,value_);
}
